##############################################################################
# chatbox.py
#
# The chatbox is the global chat system for the board. It uses the 'txt' flag
# for its server interface.
##############################################################################

import bgl, widget, re, random

import pygame
from pygame.locals import *

CONNECT = re.compile('^(?:connect )(.*) (\d*)',2)
LOG = re.compile('^(?:log )(.*)',2)
ROLL = re.compile('^(?:roll )(\d*)d(-?[\d]+)(?:([-{0,1}+{0,1}][\d]+))?',re.S)

# The chat box is the primary means of interfacing with the server.
# Other widgets can emulate some of its functionality, though.
class Chatbox(widget.Widget):
    def __init__(self, control):
        widget.Widget.__init__(self)
        self.control = control
        self.rect = pygame.Rect( (100,200,400,400) )        
        
        self.message_rect = pygame.Rect( (0,0,400,380) )
        self.message_buffer = []
        self.input_rect = pygame.Rect( (0,381,400,19) )
        self.input_buffer = ""
        
        fname = bgl.filename("data/images","FreeSans.ttf")
        self.font = bgl.load_font(fname, 14)
        self.logfile = None
        
        self.input_history = []
        self.history_pos = -1
        self.colors =  [(125,0,0),(0,125,0),(0,0,125)]
        self.colors += [(125,125,0),(125,0,125),(0,125,125)]
    
    # Update the chat box with new information from the server.
    def update(self):
        data = self.control.get("txt")
        for d in data:
            self.dirty = True
            
            ind = d.find("_")
            
            msg = d[ind+1:]
            num = d[2:ind]
            
            if num.isdigit():
                msg = "[P"+num+":] "+msg
            else:
                msg = d
                
            addition = bgl.wordwrap(msg, self.font, self.message_rect.w)
            if addition == []: addition = [""]
            for m in addition:
                self.message_buffer.append( (num,m) )
            
            if self.logfile != None:
                try:    self.logfile.write(msg+"\r")
                except:
                    self.control.push("txt","Logfile closed.")
                    self.logfile = None
        
        while len(self.input_history) > 10:
            self.input_history.pop()
    
    # Take the SDL events and work with them.
    def parse_events(self, events):
        keys = pygame.key.get_pressed()
        pos = pygame.mouse.get_pos()
        mkeys = pygame.mouse.get_pressed()
        
        x = pos[0] - self.rect.topleft[0]
        y = pos[1] - self.rect.topleft[1]
        rpos = x,y
        
        for event in events:
            if event.type == KEYDOWN:
                self.dirty = True
                if event.key == K_RETURN:
                    if self.input_buffer == "":
                        pass
                    elif self.input_buffer.startswith("%"):
                        self.parse_command(self.input_buffer[1:])
                    elif self.input_buffer.startswith("/"):
                        if self.control.alive():
                            self.control.send( "com"+self.input_buffer[1:] )
                        else:
                            m = "Not connected..."
                            self.control.push("txt",m)
                    else:
                        self.control.send( "txt"+self.input_buffer )
                        msg = "> "+self.input_buffer
                        addition = bgl.wordwrap(msg, self.font, self.rect.w)
                        for m in addition:
                            self.message_buffer.append( (-1,m) )
                            if self.logfile != None:
                                try:    self.logfile.write(m+"\r")
                                except:
                                    self.control.push("txt","Logfile closed.")
                                    self.logfile = None
                    if self.history_pos != 0:
                        self.input_history.insert(0,self.input_buffer)
                    self.input_buffer = ""
                    self.history_pos = -1
                elif event.key == K_UP:
                    if self.history_pos+1 < len(self.input_history):
                        self.history_pos += 1
                        s = self.input_history[self.history_pos]
                        self.input_buffer = s
                elif event.key == K_DOWN:
                    if self.history_pos > 0:
                        self.history_pos -= 1
                        if self.history_pos > -1:
                            s = self.input_history[self.history_pos]
                            self.input_buffer = s
                    if self.history_pos == -1:
                        self.input_buffer = ""
                elif event.key == K_BACKSPACE:
                    i = self.input_buffer
                    self.input_buffer = i[:len(i)-1]
                    self.history_pos = -1
                else:
                    try:
                        self.input_buffer += str(event.unicode)
                        self.history_pos = -1
                    except: pass
    
    # Draw the chat board on the Desktop.
    def draw(self, target):
        dist = self.rect.topleft
        mr = self.message_rect.move(dist)
        ir = self.input_rect.move(dist)
        c = (255,255,255)
        pygame.draw.rect(target, c, mr, 0)
        if self.focus: c = (205,205,235)
        pygame.draw.rect(target, c, ir, 0)
        
        v =  ""
        if self.focus: v = "|"
        text = self.font.render(self.input_buffer+v, 1, (0,0,0))
        w,h = text.get_size()
        if w > ir.w:
            srect = pygame.Rect( (0,0), ir.size )
            srect.right = text.get_rect().right
            target.blit( text, ir.move(1,1), srect )
        else:
            target.blit( text, ir.move(1,1) )
        
        while len(self.message_buffer) > mr.h/15:
            self.message_buffer.pop(0)
        
        y = 1
        for m in self.message_buffer:
            t = self.font.render(m[1], 1, (0,0,0))
            w,h = t.get_size()
            if w > mr.w:
                srect = pygame.Rect( (0,0), ir.size )
                target.blit( t, mr.move(1,y), srect )
            else:
                target.blit( t, mr.move(1,y) )
            y += 15
    
    # Parse a local text command (these are never sent to the server).
    def parse_command(self, command):
        s = command.strip()
        if len(s) == 0:
            return
        
        # Connect to the server.
        result = CONNECT.findall(s)
        if len(result) == 1:
            foo = self.control.connect(result[0][0],int(result[0][1]))
            if not foo:
                m = "Failed to connect to the host at "+result[0][0]+":"
                m += result[0][1]+"."
                self.control.push("txt",m)
            return
        
        # Start logging the chat session.
        result = LOG.findall(s)
        if len(result) == 1:
            try:
                self.logfile = file(result[0], "ab")
                self.control.push("txt","Logging to "+result[0]+".")
            except:
                self.logfile = None
                self.control.push("txt","Error logging to "+result[0][0]+".")
            return
        
        # Privately roll a die. This is perfect for GMs who don't want to
        # run another engine while trying to work in PyTaTo. Syntax is
        # just like the public die roll except it starts with a %.
        result = ROLL.findall(s)
        print result
        if len(result) == 1 and result[0][1] != "":
            numDice = 1
            if result[0][0].isdigit(): numDice = int(result[0][0])
            r = 0
            hi,lo = int(result[0][1]) , 1
            for i in range(numDice):
                r += random.randint(lo,hi)
            dr = str(numDice)+"d"
            if result[0][2] != "":
				mod = int(result[0][2])
				r += mod
				if mod >= 0:
					dr += str(hi)+'+'+str(mod)
				else:
					dr += str(hi)+str(mod)
            else:
                dr += str(hi)
            m = "You rolled a "+str(r)+" on "+dr+"."
            self.control.push("txt",m)
            return
            
        m = "Failed to execute the command: '"+command+"'"
        self.control.push("txt",m)
